Wireless Sensor data Monitoring with FlashAir

Latest update: June 2017

In this tutorial, we'll go over how to use a FlashAir card as a simple wireless connection for a sensor device.

System Requirements

  • FlashAir
  • Arduino
  • SD card shield
  • Sensor
  • Power source
  • Outer case

System Example

The example to the right is using the following equipment:

  • FlashAir W-02 (Any size will work)
  • Arduino Uno R3
  • Seeed Studio SD Shield v4.0
  • Seeed Studio Grove system push button
  • Power box (Over 7V is recommended)
  • Outer case (Acrylic box with a hinge)
arduino-sensor

Description

Description
  1. Open your web browser and connect to http://flashair/.
    • You will need to create an HTML template (a brower utility) in advance.
  2. Javascript will read the file every second or so and generate a graph.
  3. Sensor data is appended to the file using SD.h.

Source code

Arduino side

#include <SD.h>

const int SOUND_SENSOR = A5;
const int LED = 9;
int val = 0;

const int TIMER_COUNTER = 34286; // 2Hz. (65536 - 16MHz/256scale/2Hz)

const uint8_t SLOT_SIZE = 2;

File myFile;

void setup()
{
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  Serial.print("Initializing SD card...");
  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");

  if (SD.exists("TEST.TXT")) {
    Serial.println("TEST.TXT exists.");
    Serial.println("Removing TEST.TXT...");
    SD.remove("TEST.TXT");
  }
  else {
    Serial.println("TEST.TXT doesn't exist.");
  }

  pinMode(LED, OUTPUT);
  pinMode(SOUND_SENSOR, INPUT);

  // initialize Timer1
  noInterrupts(); // disable all interrupts
  TCCR1A  = 0;
  TCCR1B  = 0;
  TCNT1   = TIMER_COUNTER;
  TCCR1B |= (1 << CS12);
  TIMSK1 |= (1 << TOIE1); // enable timer overflow interrupt
  interrupts(); // enable all interrupts
}
uint16_t counter = 0;
uint8_t  slot = 0;
bool     readyToSave = false;
uint16_t value[2][SLOT_SIZE];

ISR(TIMER1_OVF_vect)
{
  value[slot][counter] = analogRead(SOUND_SENSOR);
  if (++counter == SLOT_SIZE) {
    readyToSave = true;
    counter = 0;
    slot ^= 1;
  }
  TCNT1 = TIMER_COUNTER; // preload timer
}

void save(uint8_t slotToSave) {
  myFile = SD.open("TEST.TXT", FILE_WRITE);
  if (myFile) {
    for (int i = 0; i < SLOT_SIZE - 1; i++) {
      myFile.println(value[slotToSave][i]);
    }
    myFile.println(value[slotToSave][SLOT_SIZE - 1]);
    myFile.close();
    Serial.println(".");
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening TEST.TXT");
  }
}

void loop()
{
  if (readyToSave) {
    save(slot ^ 1);
    readyToSave = false;
  }
  delay(100);
}

We use interrupts to keep up with fast changes on the sensor, but this is unnecessary if you're using a push button.

HTML template side

<!DOCTYPE html>
<html>
  <head>
  <meta charset="UTF-8">
  <script type="text/javascript" src="/SD_WLAN/jquery-2.1.0.min.js"></script>      
  <script type="text/javascript" src="/SD_WLAN/ccchart-min.js"></script>      
  </head>
  <body style="width: 99%; height: 99%; margin: 0 auto; background-color:#FFA;">
  <div id="drawing">
    <canvas id="graph" width="240" height="360" style="width:99%; max-width: 400px; height:99%; max-height: 600px;"></canvas>
  </div>

  <script type="text/javascript">
function drawBars(testData) {
  var canvas = $('#graph');
  var chartdata = {

    'config': {
    'title': 'FlashAir x Sensor',
    'titleFont': '100 16px "Arial"',
    'type': 'line',
    'lineWidth': 4,
    'minY': 0,
    'maxY': 1100,
    'useVal': 'yes',
    'onlyChartWidthTitle': 'yes',
    'colorSet': ['yellow'],
    'bgGradient': {
        'direction':'vertical',
        'from':'#333',
        'to':'#000'
      },
    'width': canvas.width(),
    'height': canvas.height()
    },

    'data': [
      ['time',1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],
      ['brightness',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    ]
  };
 var values = testData.trim().split('\n');
  var MAX_BARS = 20;
  var first = Math.max(values.length - MAX_BARS, 0);
  var last  = values.length;
  for (var i = first; i < last; i++) {
    var n = i - first;
    chartdata['data'][1][1 + n] = values[i];
  }

  ccchart.init('graph', chartdata)
}

function repeater() {
  $.ajax({
    type: 'GET',
    url: 'http://flashair.local/TEST.TXT?TIME=' + Math.floor(Math.random() * 10000),
    datatype: 'text',
    success: function(data, dataType) {
      drawBars(data);
      setTimeout('repeater()', 800);
    },
    error: function(request, status, error) {
      setTimeout('repeater()', 800);
    },
  });
}

window.onload = function() {
  var data = "100\n200\n300\n400\n500\n600\n700\n800\n900\n1000\n100\n200\n300\n400\n500\n600\n700\n800\n900\n1000\n";
  drawBars(data);

  repeater();
}
  </script>
</body>
</html>

This tutorial uses the following libraries.

Sensor data file (TEXT.TXT)

One number per line

Sensor data file

Clear the file when Arduino runs. (Delete the file)

  • It may be better to use a ring buffer method if you want to save large amounts of data.